package com.leetroll.common.utils;

import com.leetroll.common.constant.FileConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * 文件操作工具类
 */
@Slf4j
public class FileUtils {

    public static final String[] IMG_FILE = {
            FileConstant.FILE_SUFFIX_BMP,
            FileConstant.FILE_SUFFIX_JPG,
            FileConstant.FILE_SUFFIX_PNG,
            FileConstant.FILE_SUFFIX_TIF,
            FileConstant.FILE_SUFFIX_GIF,
            FileConstant.FILE_SUFFIX_JPEG,
            FileConstant.FILE_SUFFIX_WEBP};
    public static final String[] DOC_FILE = {
            FileConstant.FILE_SUFFIX_DOC,
            FileConstant.FILE_SUFFIX_DOCX,
            FileConstant.FILE_SUFFIX_TXT,
            FileConstant.FILE_SUFFIX_HLP,
            FileConstant.FILE_SUFFIX_WPS,
            FileConstant.FILE_SUFFIX_RTF,
            FileConstant.FILE_SUFFIX_XLS,
            FileConstant.FILE_SUFFIX_XLSX,
            FileConstant.FILE_SUFFIX_PPT,
            FileConstant.FILE_SUFFIX_PPTX,
            FileConstant.FILE_SUFFIX_JAVA,
            FileConstant.FILE_SUFFIX_HTML,
            FileConstant.FILE_SUFFIX_PDF,
            FileConstant.FILE_SUFFIX_MD,
            FileConstant.FILE_SUFFIX_SQL,
            FileConstant.FILE_SUFFIX_CSS,
            FileConstant.FILE_SUFFIX_JS,
            FileConstant.FILE_SUFFIX_VUE,
            FileConstant.FILE_SUFFIX_JAVA};
    public static final String[] VIDEO_FILE = {
            FileConstant.FILE_SUFFIX_AVI,
            FileConstant.FILE_SUFFIX_MP4,
            FileConstant.FILE_SUFFIX_MPG,
            FileConstant.FILE_SUFFIX_MOV,
            FileConstant.FILE_SUFFIX_SWF,
            FileConstant.FILE_SUFFIX_3GP,
            FileConstant.FILE_SUFFIX_RM,
            FileConstant.FILE_SUFFIX_RMVB,
            FileConstant.FILE_SUFFIX_WMV,
            FileConstant.FILE_SUFFIX_MKV};
    public static final String[] MUSIC_FILE = {
            FileConstant.FILE_SUFFIX_WAV,
            FileConstant.FILE_SUFFIX_AIF,
            FileConstant.FILE_SUFFIX_AU,
            FileConstant.FILE_SUFFIX_MP3,
            FileConstant.FILE_SUFFIX_RAM,
            FileConstant.FILE_SUFFIX_WMA,
            FileConstant.FILE_SUFFIX_MMF,
            FileConstant.FILE_SUFFIX_AMR,
            FileConstant.FILE_SUFFIX_AAC,
            FileConstant.FILE_SUFFIX_FLAC};
    public static String[] ALL_FILE = {
            FileConstant.FILE_SUFFIX_BMP,
            FileConstant.FILE_SUFFIX_JPG,
            FileConstant.FILE_SUFFIX_PNG,
            FileConstant.FILE_SUFFIX_TIF,
            FileConstant.FILE_SUFFIX_GIF,
            FileConstant.FILE_SUFFIX_JPEG,
            FileConstant.FILE_SUFFIX_WEBP,

            FileConstant.FILE_SUFFIX_DOC,
            FileConstant.FILE_SUFFIX_DOCX,
            FileConstant.FILE_SUFFIX_TXT,
            FileConstant.FILE_SUFFIX_HLP,
            FileConstant.FILE_SUFFIX_WPS,
            FileConstant.FILE_SUFFIX_RTF,
            FileConstant.FILE_SUFFIX_XLS,
            FileConstant.FILE_SUFFIX_XLSX,
            FileConstant.FILE_SUFFIX_PPT,
            FileConstant.FILE_SUFFIX_PPTX,
            FileConstant.FILE_SUFFIX_JAVA,
            FileConstant.FILE_SUFFIX_HTML,
            FileConstant.FILE_SUFFIX_PDF,
            FileConstant.FILE_SUFFIX_MD,
            FileConstant.FILE_SUFFIX_SQL,
            FileConstant.FILE_SUFFIX_CSS,
            FileConstant.FILE_SUFFIX_JS,
            FileConstant.FILE_SUFFIX_VUE,
            FileConstant.FILE_SUFFIX_JAVA,

            FileConstant.FILE_SUFFIX_AVI,
            FileConstant.FILE_SUFFIX_MP4,
            FileConstant.FILE_SUFFIX_MPG,
            FileConstant.FILE_SUFFIX_MOV,
            FileConstant.FILE_SUFFIX_SWF,
            FileConstant.FILE_SUFFIX_3GP,
            FileConstant.FILE_SUFFIX_RM,
            FileConstant.FILE_SUFFIX_RMVB,
            FileConstant.FILE_SUFFIX_WMV,
            FileConstant.FILE_SUFFIX_MKV,

            FileConstant.FILE_SUFFIX_WAV,
            FileConstant.FILE_SUFFIX_AIF,
            FileConstant.FILE_SUFFIX_AU,
            FileConstant.FILE_SUFFIX_MP3,
            FileConstant.FILE_SUFFIX_RAM,
            FileConstant.FILE_SUFFIX_WMA,
            FileConstant.FILE_SUFFIX_MMF,
            FileConstant.FILE_SUFFIX_AMR,
            FileConstant.FILE_SUFFIX_AAC,
            FileConstant.FILE_SUFFIX_FLAC
    };

    /**
     * 上传文件
     *
     * @param pathRoot 物理路径webapp所在路径
     * @return 返回图片上传的地址
     */
    public static String uploadFile(String pathRoot, String baseUrl, MultipartFile avatar) throws IllegalStateException, IOException {
        String path = "";
        if (!avatar.isEmpty()) {
            //获得文件类型（可以判断如果不是图片，禁止上传）  
            String contentType = avatar.getContentType();
            if (contentType != null && contentType.contains("image")) {
                //获得文件后缀名称
                String imageName = contentType.substring(contentType.indexOf("/") + 1);
                path = baseUrl + StringUtils.getUUID() + "." + imageName;
                avatar.transferTo(new File(pathRoot + path));
            }
        }
        return path;
    }

    /**
     * 根据OriginalFilename获取FileName【去除文件后缀】
     *
     * @param originalFilename 文件名
     */
    public static String getFileNameWithoutSuffix(String originalFilename) {
        String fileName = "";
        if (StringUtils.isNotBlank(originalFilename) &&
                StringUtils.contains(originalFilename, ".")) {
            fileName = originalFilename.substring(0, originalFilename.lastIndexOf("."));
        }
        return fileName;
    }

    /**
     * 从Request中获取文件
     */
    public static List<MultipartFile> getMultipartFileList(HttpServletRequest request) {
        List<MultipartFile> files = new ArrayList<>();
        try {
            CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
                    request.getSession().getServletContext());
            if (request instanceof MultipartHttpServletRequest) {
                // 将request变成多部分request
                MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
                Iterator<String> iter = multiRequest.getFileNames();
                // 检查form中是否有enctype="multipart/form-data"
                if (multipartResolver.isMultipart(request) && iter.hasNext()) {
                    // 获取multiRequest 中所有的文件名
                    while (iter.hasNext()) {
                        List<MultipartFile> fileRows = multiRequest
                                .getFiles(iter.next());
                        if (fileRows.size() != 0) {
                            for (MultipartFile file : fileRows) {
                                if (file != null && !file.isEmpty()) {
                                    files.add(file);
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception ex) {
            log.error("解析MultipartRequest错误", ex);
        }
        return files;
    }


    /**
     * 判断一个文件是否存在，不存在就创建它 Method execute,只能建最后面那个目录
     *
     * @param path 文件路径
     */
    public static void creatFile(String path) {
        File file = new File(path);
        if (file.isDirectory()) {
            file.mkdirs();
        } else {
            file.mkdir();
        }
    }

    /**
     * 创建文件夹 (多层级)
     */
    public static void creatDir(String path) {
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    /**
     * 从文件名中得到其后缀名
     *
     * @param filename 文件名
     * @return 后缀名
     */
    public static String getFileSuffix(String filename) {
        String suffix;
        suffix = filename.substring(
                filename.lastIndexOf(FileConstant.SYMBOL_POINT) + 1);
        return suffix;
    }

    /**
     * 通过其后缀名判断其是否合法,合法后缀名为常见的
     *
     * @param suffix 后缀名
     * @return 合法返回true，不合法返回false
     */
    public static boolean isSafe(String suffix) {
        suffix = suffix.toLowerCase();
        for (String s : ALL_FILE) {
            if (s.equals(suffix)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 通过其后缀名判断其是否是图片
     *
     * @param suffix 后缀名
     * @return 合法返回true，不合法返回false
     */
    public static boolean isPic(String suffix) {
        suffix = suffix.toLowerCase();
        for (String s : IMG_FILE) {
            if (s.equals(suffix)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 计算文件大小，将long类型转换为String类型
     *
     * @param fileSize 字节大小
     */
    public static String getFileStringSize(long fileSize) {
        //size不能为0？
        double temp = 0.0;
        String ssize = "";
        temp = (double) fileSize / FileConstant.NUM_1024;
        if (temp >= FileConstant.NUM_1024) {
            temp = temp / FileConstant.NUM_1024;
            if (temp >= FileConstant.NUM_1024) {
                temp = temp / FileConstant.NUM_1024;
                ssize = temp + "000";
                ssize = ssize.substring(FileConstant.NUM_ZERO, ssize.indexOf(FileConstant.SYMBOL_POINT) + FileConstant.NUM_THREE) + "GB";
            } else {
                ssize = temp + "000";
                ssize = ssize.substring(FileConstant.NUM_ZERO, ssize.indexOf(FileConstant.SYMBOL_POINT) + FileConstant.NUM_THREE) + "MB";
            }
        } else {
            ssize = temp + "000";
            ssize = ssize.substring(FileConstant.NUM_ZERO, ssize.indexOf(FileConstant.SYMBOL_POINT) + FileConstant.NUM_THREE) + "KB";
        }
        return ssize;
    }

    /**
     * 拷贝文件夹
     *
     * @param sourcePath 源地路径
     * @param targetPath 目标路径
     */
    public static void copyFolder(String sourcePath, String targetPath) {
        File sourceFolder = new File(sourcePath);
        if (!sourceFolder.exists()) {
            log.error("Source folder does not exist");
        }

        File destinationFolder = new File(targetPath);
        if (!destinationFolder.exists()) {
            destinationFolder.mkdirs();
        } else if (destinationFolder.exists() && !destinationFolder.isDirectory()) {
            throw new IllegalArgumentException("Destination is already a file.");
        }

        for (File file : Objects.requireNonNull(sourceFolder.listFiles())) {
            if (file.isDirectory()) {
                copyFolder(file.getAbsolutePath(), destinationFolder + "/" + file.getName());
            } else {
                try {
                    copyFile(file.getAbsolutePath(), destinationFolder + "/" + file.getName());
                } catch (IOException e) {
                    log.error("拷贝文件出错 {}", e.getMessage());
                }
            }
        }
    }

    /**
     * 拷贝文件
     *
     * @param sourcePath 源地路径
     * @param targetPath 目标路径
     */
    public static void copyFile(String sourcePath, String targetPath) throws IOException {
        File targetFile = new File(targetPath);
        if (!targetFile.exists()) targetFile.createNewFile();

        try (InputStream inputStream = Files.newInputStream(new File(sourcePath).toPath());
             OutputStream outputStream = Files.newOutputStream(targetFile.toPath())) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, length);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 压缩文件夹
     *
     * @param srcPath 源地路径
     * @param zipPath 目标路径
     */
    public static void toZip(String srcPath, String zipPath) {

        try {
            FileOutputStream fos = new FileOutputStream(zipPath);
            ZipOutputStream zos = new ZipOutputStream(fos);

            File sourceFolder = new File(srcPath);
            addFolderToZip(sourceFolder, sourceFolder.getName(), zos);

            zos.flush();
            zos.close();
            fos.flush();
            fos.close();

            System.out.println("Folder zipped successfully!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void addFolderToZip(File sourceFolder, String parentFolderName, ZipOutputStream zos) throws IOException {
        File[] files = sourceFolder.listFiles();
        byte[] buffer = new byte[1024];
        int bytesRead;

        if (files == null) return;
        for (File file : files) {
            if (file.isDirectory()) {
                addFolderToZip(file, parentFolderName + "/" + file.getName(), zos);
                continue;
            }
            FileInputStream fis = new FileInputStream(file);
            zos.putNextEntry(new ZipEntry(parentFolderName + "/" + file.getName()));

            while ((bytesRead = fis.read(buffer)) > 0) {
                zos.write(buffer, 0, bytesRead);
            }
            zos.closeEntry();
            fis.close();
        }
    }

    /**
     * 删除文件夹
     *
     * @param folder 待删除的文件夹
     */
    public static void deleteFolder(File folder) {
        if (!folder.exists()) return;
        File[] files = folder.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteFolder(file);
                } else {
                    file.delete();
                }
            }
        }
        folder.delete();
    }

    /**
     * 读取文件为字符串
     *
     * @param file 文件
     */
    public static String getStr(File file) {
        String str = "";
        try {
            FileReader fileReader = new FileReader(file);
            Reader reader = new InputStreamReader(Files.newInputStream(file.toPath()), StandardCharsets.UTF_8);
            int ch = 0;
            StringBuffer sb = new StringBuffer();
            while ((ch = reader.read()) != -1) {
                sb.append((char) ch);
            }
            fileReader.close();
            reader.close();
            str = sb.toString();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

    }
}
